08 设计模式——责任链模式

返回设计模式博客目录

介绍


责任链模式:使多个对象都有机会处理请求,从而避免了请求的发送者和接受者之间的耦合关系。将这些对象连城一条链,并沿着这条链传递该请求,直到有对象处理它为止

它是行为型设计模式之一。我们将多个节点首尾相连构成的模型称为链,而每个节点都可以拆开再连接。因此,链式结构具有很好的灵活性。将这样一种结构应用于编程领域,将每一个节点看作是一个对象,每一个对象拥有不同的处理逻辑,将一个请求从链式的首端发出,沿着链的路径依次传递给每一个节点对象,直至有对象处理这个请求为止。我们将这样的一种模式称为责任链模式

使用场景

多个对象可以处理同一请求,但具体由哪个对象处理则在运行时动态决定。
在请求处理者不明确的情况下向多个对象中的一个提交一个请求。
需要动态指定一组对象处理请求。

优缺点

优点:对请求者和处理者关系解耦,提高代码的灵活性。
缺点:如果处理者太多,那么遍历必定会影响性能。

UML 关系图

简化版 UML 类图

根据类图我们可以得出如下简化版的通用模板代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// 抽象处理者
abstract class Handler {
protected Handler successor; // 下一个节点的处理者
/**
* 请求处理
* @param condition 请求条件
*/
public abstract void handleRequest(String condition);
}
// 具体的处理者 1
class ConcreteHandler1 extends Handler {
@Override
public void handleRequest(String condition) {
if ("ConcreteHandler1".equals(condition)) {
System.out.println("ConcreteHandler1 handled");
} else {
successor.handleRequest(condition);
}
}
}
// 具体的处理者 2
class ConcreteHandler2 extends Handler {
@Override
public void handleRequest(String condition) {
if ("ConcreteHandler2".equals(condition)) {
System.out.println("ConcreteHandler2 handled");
} else {
successor.handleRequest(condition);
}
}
}
// 客户类
public class Client {
public static void main(String[] args) {
ConcreteHandler1 handler1 = new ConcreteHandler1();
ConcreteHandler2 handler2 = new ConcreteHandler2();
handler1.successor = handler2;
handler2.successor = handler1;
handler1.handleRequest("ConcreteHandler2");
}
}

角色介绍:

  • Handler:抽象处理者角色,声明一个请求处理的方法,并在其中保持一个对下一个处理节点 Handler 对象的引用。
  • ConcreteHandler:具体处理者角色,对请求进行处理,如果不能处理则将该请求转发给下一个节点上的处理对象。
完整版 UML 类图

上面的请求形式为固定的字符串,处理规则为该字符串是否与之匹配。然而在大多数情况下,责任链中的请求和对应的处理规则是不尽相同的,在这种情况下可以将请求进行封装,同时对请求的处理规则也进行封装作为一个独立的对象。类图如下:

对应模板代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
// 抽象处理者
abstract class AbstractHandler {
protected AbstractHandler nextHandler; // 下一个节点上的处理者对象
/**
* 处理请求
* @param request 请求对象
*/
public final void handleRequest(AbstractRequest request) {
if (getHandlerLevel() == request.getRequestLevel()) {
// 一致则由该处理对象处理
handle(request);
} else {
// 否则将该请求转发给下一个节点上的请求对象
if (nextHandler != null) {
nextHandler.handleRequest(request);
} else {
System.out.println("All of handler can not handle the request");
}
}
}
protected abstract int getHandlerLevel();
// 每个处理者对象的具体处理方式
protected abstract void handle(AbstractRequest request);
}
// 抽象请求者
abstract class AbstractRequest {
private Object obj; // 处理对象
public AbstractRequest(Object obj) {
this.obj = obj;
}
public Object getContent() {
return obj;
}
// 获取请求级别
public abstract int getRequestLevel();
}
// 具体处理者
class Handler1 extends AbstractHandler {
@Override
protected int getHandlerLevel() {
return 1;
}
@Override
protected void handle(AbstractRequest request) {
System.out.println("Handler1 handle request: "
+ request.getRequestLevel());
}
}
class Handler2 extends AbstractHandler {
@Override
protected int getHandlerLevel() {
return 2;
}
@Override
protected void handle(AbstractRequest request) {
System.out.println("Handler2 handle request: "
+ request.getRequestLevel());
}
}
class Handler3 extends AbstractHandler {
@Override
protected int getHandlerLevel() {
return 3;
}
@Override
protected void handle(AbstractRequest request) {
System.out.println("Handler3 handle request: "
+ request.getRequestLevel());
}
}
// 具体请求者
class ConcreteRequest1 extends AbstractRequest {
public ConcreteRequest1(Object obj) {
super(obj);
}
@Override
public int getRequestLevel() {
return 1;
}
}
class ConcreteRequest2 extends AbstractRequest {
public ConcreteRequest2(Object obj) {
super(obj);
}
@Override
public int getRequestLevel() {
return 2;
}
}
class ConcreteRequest3 extends AbstractRequest {
public ConcreteRequest3(Object obj) {
super(obj);
}
@Override
public int getRequestLevel() {
return 3;
}
}

下面是客户类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Client {
public static void main(String[] args) {
// 构造 3 个处理者对象
AbstractHandler handler1 = new Handler1();
AbstractHandler handler2 = new Handler2();
AbstractHandler handler3 = new Handler3();
// 设置当前处理者对象的下一个节点的处理者对象
handler1.nextHandler = handler2;
handler2.nextHandler = handler3;
// 构造 3 个请求者对象
AbstractRequest request1 = new ConcreteRequest1("Request1");
AbstractRequest request2 = new ConcreteRequest2("Request2");
AbstractRequest request3 = new ConcreteRequest3("Request3");
// 总是从链式的首端发起请求
handler1.handleRequest(request1);
handler1.handleRequest(request2);
// 不从链式的首端发起请求
handler2.handleRequest(request3);
}
}

对于责任链中的一个处理者对象,其只有两个行为:一是处理请求,而是将请求转发给下一个节点。不允许某个处理者对象在处理了请求后又将请求转发给上一个节点的情况。对于一条责任链来说,一个请求最终只有两种情况:一是被某个处理对象所处理,另一个是所有对象均未对其处理。前一种情况称之为纯的责任链,后一种情况称之为不纯的责任链。

示例


小先向组长报销 5 万元费用,组长一看是笔不小的数目,他没有权限审批,于是组长拿着票据去找部门主管;主管的权限内只能批五千以下的费用,于是主管又跑去找经理;经理权限也不够直接奔向老板的办公室。使用责任链模式的代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
// 抽象领导者
abstract class Leader {
protected Leader nextHandler; // 上一级领导处理者
/**
* 处理报账请求
* @param money 能批复的报账额度
*/
public final void handleRequest(int money) {
if (money <= limit()) {
handle(money);
} else {
if (nextHandler != null) {
nextHandler.handleRequest(money);
}
}
}
// 自身能批复的额度权限
protected abstract int limit();
// 处理报账行为
protected abstract void handle(int money);
}
// 各个具体的领导者
class GroupLeader extends Leader {
@Override
protected int limit() {
return 1000;
}
@Override
protected void handle(int money) {
System.out.println("组长批复报销" + money + "元");
}
}
class Director extends Leader {
@Override
protected int limit() {
return 5000;
}
@Override
protected void handle(int money) {
System.out.println("主管批复报销" + money + "元");
}
}
class Manager extends Leader {
@Override
protected int limit() {
return 10000;
}
@Override
protected void handle(int money) {
System.out.println("经理批复报销" + money + "元");
}
}
class Boss extends Leader {
@Override
protected int limit() {
return Integer.MAX_VALUE;
}
@Override
protected void handle(int money) {
System.out.println("老板批复报销" + money + "元");
}
}
// 客户类
public class Client {
public static void main(String[] args) {
// 构造各个领导对象
GroupLeader groupLeader = new GroupLeader();
Director director = new Director();
Manager manager = new Manager();
Boss boss = new Boss();
// 设置上一级领导处理对象
groupLeader.nextHandler = director;
director.nextHandler = manager;
manager.nextHandler = boss;
// 发起报账请求
groupLeader.handleRequest(50000);
}
}

责任链模式的灵活之处在于请求的发起可以从责任链的任何一个节点处开始,同时也可以改变责任链内部传递的规则。比如,直接越过组长找主管报账,或者直接将经理设置为组长的上一级节点。

ANDROID 源码中的责任链模式实现


责任链模式在 ANDROID 源码中比较类似的实现莫过于对事件的分发处理,每当用户接触屏幕时,ANDROID 都会将对应的事件包装成一个事件对象从 ViewTree 的顶部自上而下地分发传递。而 ViewGroup 中执行事件派发的方法是 dispatchTouchEvent,在该方法中其对事件进行了统一的分发。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
public boolean dispatchTouchEvent(MotionEvent ev) {
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
}
// 对于辅助功能的事件处理
if (ev.isTargetAccessibilityFocus() && isAccessibilityFocusedViewOrHost()) {
ev.setTargetAccessibilityFocus(false);
}
boolean handled = false;
if (onFilterTouchEventForSecurity(ev)) {
final int action = ev.getAction();
final int actionMasked = action & MotionEvent.ACTION_MASK;
// 处理原始的 DOWN 事件
if (actionMasked == MotionEvent.ACTION_DOWN) {
// 这里主要是在新事件开始时处理完上一个事件
cancelAndClearTouchTargets(ev);
resetTouchState();
}
// 检查事件拦截
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action); // 恢复事件防止其改变
} else {
intercepted = false;
}
} else {
intercepted = true;
}
// 如果事件被拦截了,则进行正常的事件分发
if (intercepted || mFirstTouchTarget != null) {
ev.setTargetAccessibilityFocus(false);
}
// 检查事件是否取消
final boolean canceled = resetCancelNextUpFlag(this)
|| actionMasked == MotionEvent.ACTION_CANCEL;
// 如果有必要的话,为 DOWN 事件检查所有的目标对象
final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
TouchTarget newTouchTarget = null;
boolean alreadyDispatchedToNewTouchTarget = false;
// 如果事件未被取消且未被拦截
if (!canceled && !intercepted) {
// 如果有辅助功能的参与,则直接将事件投递到对应的 View
// 否则将事件分发给所有的子 View
View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus()
? findChildWithAccessibilityFocus() : null;
// 如果事件为起始事件
if (actionMasked == MotionEvent.ACTION_DOWN
|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
final int actionIndex = ev.getActionIndex(); // always 0 for down
final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
: TouchTarget.ALL_POINTER_IDS;
removePointersFromTouchTargets(idBitsToAssign);
final int childrenCount = mChildrenCount;
// 如果 newTouchTarget 为空且子元素不为 0
if (newTouchTarget == null && childrenCount != 0) {
final float x = ev.getX(actionIndex);
final float y = ev.getY(actionIndex);
// 自上而下去寻找一个可以接收该事件的子 View
final ArrayList<View> preorderedList = buildTouchDispatchChildList();
final boolean customOrder = preorderedList == null
&& isChildrenDrawingOrderEnabled();
final View[] children = mChildren;
// 遍历子元素
for (int i = childrenCount - 1; i >= 0; i--) {
final int childIndex = getAndVerifyPreorderedIndex(
childrenCount, i, customOrder);
final View child = getAndVerifyPreorderedView(
preorderedList, children, childIndex);
if (childWithAccessibilityFocus != null) {
if (childWithAccessibilityFocus != child) {
continue;
}
childWithAccessibilityFocus = null;
i = childrenCount - 1;
}
// 如果子元素无法接收 PointerEvent 或这个事件点压根就没有
// 落在子元素的边界范围内,那么就跳出该次循环继续遍历
if (!canViewReceivePointerEvents(child)
|| !isTransformedTouchPointInView(x, y, child, null)) {
ev.setTargetAccessibilityFocus(false);
continue;
}
// 找到 Event 该由哪个子元素持有
newTouchTarget = getTouchTarget(child);
if (newTouchTarget != null) {
newTouchTarget.pointerIdBits |= idBitsToAssign;
break;
}
resetCancelNextUpFlag(child);
// 投递事件执行触摸操作
// 如果子元素还是一个 ViewGroup,则递归调用重复此过程
// 如果子元素是一个 View,那么会调用 View 的 dispatchTouchEvent,
// 并最终由 onTouchEvent 处理
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
// 子 View 在其边界范围内接收该事件
mLastTouchDownTime = ev.getDownTime();
if (preorderedList != null) {
for (int j = 0; j < childrenCount; j++) {
if (children[childIndex] == mChildren[j]) {
mLastTouchDownIndex = j;
break;
}
}
} else {
mLastTouchDownIndex = childIndex;
}
mLastTouchDownX = ev.getX();
mLastTouchDownY = ev.getY();
newTouchTarget = addTouchTarget(child, idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;
break;
}
ev.setTargetAccessibilityFocus(false);
}
if (preorderedList != null) preorderedList.clear();
}
// 如果没有发现子元素可以持有该次事件
if (newTouchTarget == null && mFirstTouchTarget != null) {
newTouchTarget = mFirstTouchTarget;
while (newTouchTarget.next != null) {
newTouchTarget = newTouchTarget.next;
}
newTouchTarget.pointerIdBits |= idBitsToAssign;
}
}
}
if (mFirstTouchTarget == null) {
// 重点:dispatchTransformedTouchEvent
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
} else {
TouchTarget predecessor = null;
TouchTarget target = mFirstTouchTarget;
while (target != null) {
final TouchTarget next = target.next;
if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
handled = true;
} else {
final boolean cancelChild = resetCancelNextUpFlag(target.child)
|| intercepted;
// 重点:dispatchTransformedTouchEvent
if (dispatchTransformedTouchEvent(ev, cancelChild,
target.child, target.pointerIdBits)) {
handled = true;
}
if (cancelChild) {
if (predecessor == null) {
mFirstTouchTarget = next;
} else {
predecessor.next = next;
}
target.recycle();
target = next;
continue;
}
}
predecessor = target;
target = next;
}
}
...
}
...
return handled;
}

这里我们主要看看 dispatchTransformedTouchEvent 方法是如何调度子元素 dispatchTouchEvent 方法的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
final boolean handled;
final int oldAction = event.getAction();
// 如果事件被取消
if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
event.setAction(MotionEvent.ACTION_CANCEL);
// 如果没有子元素,则会调用父类的 dispatchTouchEvent。这里的父类终会为 View 类
if (child == null) {
handled = super.dispatchTouchEvent(event);
} else {
// 如果有子元素则传递 cancel 事件
handled = child.dispatchTouchEvent(event);
}
event.setAction(oldAction);
return handled;
}
// 计算即将被传递的点的数量
final int oldPointerIdBits = event.getPointerIdBits();
final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;
// 如果没有相应的点,那么就丢弃该事件
if (newPointerIdBits == 0) {
return false;
}
// 声明临时变量保存坐标转换后的 MotionEvent
final MotionEvent transformedEvent;
if (newPointerIdBits == oldPointerIdBits) {
// 如果子元素为空或者有一个单位矩阵
if (child == null || child.hasIdentityMatrix()) {
if (child == null) {
// 为空则调用父类 dispatchTouchEvent
handled = super.dispatchTouchEvent(event);
} else {
// 否则尝试获取 xy 方向上的偏移量(如果通过 scrollTo 或 scrollBy
// 对子视图进行滚动的话)
final float offsetX = mScrollX - child.mLeft;
final float offsetY = mScrollY - child.mTop;
// 将 MotionEvent 进行坐标变换
event.offsetLocation(offsetX, offsetY);
// 再将变换后的 MotionEvent 传递给子元素
handled = child.dispatchTouchEvent(event);
// 复位 MotionEvent 以便以后再次使用
event.offsetLocation(-offsetX, -offsetY);
}
// 如果通过以上的逻辑判断,当前事件被持有则可以直接返回
return handled;
}
transformedEvent = MotionEvent.obtain(event);
} else {
transformedEvent = event.split(newPointerIdBits);
}
// Perform any necessary transformations and dispatch.
if (child == null) {
handled = super.dispatchTouchEvent(transformedEvent);
} else {
final float offsetX = mScrollX - child.mLeft;
final float offsetY = mScrollY - child.mTop;
transformedEvent.offsetLocation(offsetX, offsetY);
if (! child.hasIdentityMatrix()) {
transformedEvent.transform(child.getInverseMatrix());
}
handled = child.dispatchTouchEvent(transformedEvent);
}
// Done.
transformedEvent.recycle();
return handled;
}

ViewGroup 事件投递的递归调用就类似于一条责任链,一旦其寻找到责任者,那么将由责任者持有并消费掉该次事件,具体地体现在 View 的 onTouchEvent 方法中返回值的设置,如果 onTouchEvent 返回 false,那么意味着当前 View 不会是该次事件的责任人,将不会持有;如果为 true 则相反,此时 View 会持有该事件并不再向外传递。

责任链模式实战


ANDROID 中的 BroastCast 分为两种,一种是普通广播,另一种是有序广播。普通广播是异步的,发出时可以被所有的接收者收到。而有序广播是根据优先级依次传播的,直到有接收者将其终止或者所有接收者都不终止它。有序广播的这一特性与我们的责任链模式很相近,我们可以轻松地实现一种全局的责任链事件处理。

1bba81880d9c47348a0ea7877326c4f5.gif